if not ButtonKeeper then
  ButtonKeeper = { };
end
if not ButtonKeeperOptions then
  ButtonKeeperOptions = { };
end

local MAX_BUTTONS = 120;
local MAX_MACROS = 18;

------------------------------------
-- Manage button assignments
------------------------------------

ButtonKeeper.StoreButtons = function(name, printToUi)
  if name ~= nil and strlen(name) > 0 then
    local actionType, actionId, actionSubtype, actionText;
    local macroName, macroIconTexture, macroBody;
    local spellName, spellRank;
    
    local buttonset = {};
    local buttonslot = 0;
    for buttonslot = 1, MAX_BUTTONS do
      local buttoninfo = {};
      actionType, actionId, actionSubtype = GetActionInfo(buttonslot);
      actionText = GetActionText(buttonslot); -- name of macros
      buttoninfo["type"] = actionType;
      buttoninfo["id"] = actionId; -- item ID, spell ID, macro ID
      buttoninfo["subtype"] = actionSubtype; -- type of spellbook
      buttoninfo["text"] = actionText;
      
      if actionType == "spell" then
        spellName, spellRank = GetSpellName(actionId, "spell");
        buttoninfo["text"] = spellName;
        buttoninfo["rank"] = spellRank;
      elseif actionType == "macro" then
        macroName, macroIconTexture, macroBody = GetMacroInfo(actionId);
        buttoninfo["texture"] = macroIconTexture;
        buttoninfo["macrobody"] = macroBody;
      end
      
      buttonset[buttonslot] = buttoninfo;
    end
    ButtonKeeperOptions[name] = buttonset;
    
    ButtonKeeperOptionPanelButtonSetInfoMenu:SetValue(name);
		ButtonKeeperOptionPanelButtonSetInfoMenuLabel:SetText(name);
		
    ButtonKeeper.Print(BUTTONKEEPER_STORED..name, printToUi);
  end
end

ButtonKeeper.DeleteButtons = function(name, printToUi)
  if "default" == name then
    ButtonKeeper.Print(BUTTONKEEPER_CANNOT_DELETE_DEFAULT, printToUi);
  elseif ButtonKeeperOptions[name] ~= nil then
		ButtonKeeperOptions[name] = nil;
		
		ButtonKeeperOptionPanelButtonSetName:SetText("default");
		ButtonKeeperOptionPanelButtonSetInfoMenu:SetValue("default");
		ButtonKeeperOptionPanelButtonSetInfoMenuLabel:SetText("default");
		
	  ButtonKeeper.Print(BUTTONKEEPER_DELETED..name, printToUi);
	else
	  ButtonKeeper.Print(BUTTONKEEPER_BUTTONSET_NOT_FOUND..name, printToUi);
	end
end

ButtonKeeper.ApplyButtonSet = function(name, printToUi)
  if name == nil then
    return;
  end
  
  if InCombatLockdown() then
    ButtonKeeper.Print(BUTTONKEEPER_NO_APPLY_IN_COMBAT, printToUi);
    return;
  end
  
  local buttonset = ButtonKeeperOptions[name];
  if buttonset ~= nil then
  	local buttonslot = 0;
  	
  	local buttoninfo;
    for buttonslot = 1, MAX_BUTTONS do
      buttoninfo = buttonset[buttonslot];
      
      if buttoninfo["type"] == "spell" then
        local spellName, spellRank = GetSpellName(buttoninfo["id"], buttoninfo["subtype"]);
        if buttoninfo["text"] == spellName and buttoninfo["rank"] == spellRank then
	        ClearCursor();
	        PickupSpell(buttoninfo["id"], buttoninfo["subtype"]);
	        PlaceAction(buttonslot);
	      elseif buttoninfo["rank"] then
	        -- find the correct spell and rank
	        local spellId = 0;
	        local spellIndex = 1;
	        spellName, spellRank = GetSpellName(spellIndex, buttoninfo["subtype"]);
	        while spellName ~= nil do
				    if spellName == buttoninfo["text"] and spellRank == buttoninfo["rank"] then
				      spellId = spellIndex;
				      break;
				    end
				    
				    spellIndex = spellIndex + 1;
				    spellName, spellRank = GetSpellName(spellIndex, buttoninfo["subtype"]);
					end
					
					if spellId > 0 then
				    ClearCursor();
        		PickupSpell(spellId, buttoninfo["subtype"]);
        		PlaceAction(buttonslot);
				    break;
				  end
	      end
        
      elseif buttoninfo["type"] == "item" then
        ClearCursor();
        PickupItem(buttoninfo["id"]);
        PlaceAction(buttonslot);
        
      elseif buttoninfo["type"] == "macro" then
        local macroId = GetMacroIndexByName(buttoninfo["text"]);
        if macroId > 0 then
          ClearCursor();
        	PickupMacro(macroId);
        	PlaceAction(buttonslot);
        else
          local globalMacros, charMacros = GetNumMacros();
          if globalMacros + charMacros < MAX_MACROS*2 then
            local perCharMacro = globalMacros >= MAX_MACROS;
            
	          -- find the icon
	          local iconId, macroTexture;
	          for iconId=1, GetNumMacroIcons() do
	            macroTexture = GetMacroIconInfo(iconId);
	            if macroTexture == buttoninfo["texture"] then
	              break;
	            end
	          end
	          
	          macroId = CreateMacro(buttoninfo["text"], iconId, buttoninfo["macrobody"], false, perCharMacro);
	          ClearCursor();
        	  PickupMacro(macroId);
        	  PlaceAction(buttonslot);
	        end
        end
      end
    end
	  ButtonKeeper.Print(BUTTONKEEPER_APPLIED..name, printToUi);
  else
	  ButtonKeeper.Print(BUTTONKEEPER_BUTTONSET_NOT_FOUND..name, printToUi);
  end
end

------------------------------------
-- Event Functions
------------------------------------

ButtonKeeper.OnLoad = function()
  this:RegisterEvent("VARIABLES_LOADED");
	
  --Set up the slash command
  SLASH_ButtonKeeper1 = "/bk";
  SlashCmdList["ButtonKeeper"] = ButtonKeeper.SlashHandler;
end

ButtonKeeper.OnEvent = function(event, ...)
  if event == "VARIABLES_LOADED" then
    local menu = getglobal("ButtonKeeperOptionPanelButtonSetInfoMenu");
    if menu then
      ButtonKeeper.InfoMenuLoad(menu, "ButtonSet");
    end
    return;
  end
end

-----------------------------
-- MOD CONFIGURATION CODE
-----------------------------

ButtonKeeper.Print = function(msg, printToUi)
  if printToUi then
    local statusText = getglobal("ButtonKeeperOptionPanelStatusText");
    if statusText then
  		statusText:SetText(msg);
  	else
  	  DEFAULT_CHAT_FRAME:AddMessage(msg, 1.0, 1.0, 1.0);
  	end
	elseif DEFAULT_CHAT_FRAME then
		DEFAULT_CHAT_FRAME:AddMessage(msg, 1.0, 1.0, 1.0);
	end
end

ButtonKeeper.SlashHandler = function(command)
  local _, _, cmd, param = string.find(command, "^([^ ]+) (.+)$");

  -- nil prevention
  if (cmd == nil) then cmd = command; end
  if (not cmd) then cmd = command; end
  if (not cmd) then cmd = ""; end
  if (not param) then param = ""; end
	
  local upperCmd = string.upper(cmd);
  
  if (upperCmd == "SAVE") then
    if param == "" then
      param = "default";
    end
    ButtonKeeper.StoreButtons(param);
  elseif (upperCmd == "APPLY") then
    if param == "" then
      param = "default";
    end
    ButtonKeeper.ApplyButtonSet(param);
  elseif (upperCmd == "DEL") then
    if strlen(param) > 0 then
      ButtonKeeper.DeleteButtons(param);
    end
  elseif ((cmd == "") or (cmd == "help")) then
	  ButtonKeeper.Print(BUTTONKEEPER_USEAGE);
	end
end

------------------------------------
-- Option Menu Functions
------------------------------------

ButtonKeeper.InfoMenuLoad = function(frame, key)
  local initFunc = ButtonKeeper["InfoMenuInitialize"..key];
  if initFunc then
		UIDropDownMenu_Initialize(frame, initFunc);
		local selectedVal = "default";
		UIDropDownMenu_SetSelectedValue(frame, selectedVal);
		UIDropDownMenu_SetWidth(80, frame);
		frame.defaultValue = "default";
		frame.value = selectedVal;
		frame.currValue = frame.selectedVal;
		frame.SetValue = 
			function (self, value)
				UIDropDownMenu_SetSelectedValue(self, value);
				self.value = value;
			end
		frame.GetValue =
			function (self)
				return UIDropDownMenu_GetSelectedValue(self);
			end
	end
end

ButtonKeeper.InfoMenuClickButtonSet = function()
	ButtonKeeperOptionPanelButtonSetInfoMenu:SetValue(this.value);
	ButtonKeeperOptionPanelButtonSetName:SetText(this.value);
end

ButtonKeeper.InfoMenuInitializeButtonSet = function()
	local selectedValue = UIDropDownMenu_GetSelectedValue(ButtonKeeperOptionPanelButtonSetInfoMenu);
	local info = UIDropDownMenu_CreateInfo();
	info.func = ButtonKeeper.InfoMenuClickButtonSet;
	
	for key, val in pairs(ButtonKeeperOptions) do
	  info.text = key;
	  info.value = key;
	  if ( info.value == selectedValue ) then
			info.checked = 1;
		else
			info.checked = nil;
		end
		UIDropDownMenu_AddButton(info);
	end
end